package com.wzw.springboot.principle.spel;

import com.wzw.springboot.principle.exception.BusinessException;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParserContext;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.util.Objects;

/**
 * @author Wangzhiwen
 */
@Aspect
@Component
public class PermissionAspect implements BeanFactoryAware {

    // 表达式解析器
    private final ExpressionParser expressionParser = new SpelExpressionParser(new SpelParserConfiguration(true, true));

    // 表达式模板
    private final ParserContext parserContext = new TemplateParserContext();

    // 上下文环境
    private final StandardEvaluationContext evaluationContext = new StandardEvaluationContext();

    @Override
    @SuppressWarnings("all")
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        // 为上下文环境设置BeanResolver
        evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
    }

    // 切点
    @Pointcut("@annotation(com.wzw.springboot.principle.spel.Permission) || @within(com.wzw.springboot.principle.spel.Permission)")
    public void pointCat() {
    }

    // 切面
    @Before("pointCat()")
    public void around(JoinPoint joinPoint) {
        Permission annotation = parseSpelAnnotation(joinPoint);
        if (Objects.isNull(annotation) || Objects.isNull(annotation.value()) || annotation.value().trim().isEmpty()) {
            return;
        }
        String expressionString = annotation.value();
        Expression expression = expressionParser.parseExpression(expressionString, parserContext);
        Boolean expressionValue = expression.getValue(evaluationContext, Boolean.class);
        if (Objects.isNull(expressionValue) || !expressionValue) {
            throw new BusinessException("无权限");
        }
    }

    private Permission parseSpelAnnotation(JoinPoint joinPoint) {
        try {
            Class<?> targetClass = joinPoint.getTarget().getClass();
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            if (methodSignature.getMethod().isAnnotationPresent(Permission.class)) {
                return methodSignature.getMethod().getAnnotation(Permission.class);
            }
            if (targetClass.isAssignableFrom(Permission.class)) {
                return targetClass.getAnnotation(Permission.class);
            }
            return null;
        } catch (Exception e) {
            return null;
        }
    }
}
